package io.github.jsp.executor;

import io.github.jsp.model.JobInfo;
import io.github.jsp.model.JobLog;
import io.github.jsp.service.JobLogService;
import io.github.jsp.util.ServerIdUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.sql.Timestamp;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class JobExecutor {
    
    private static final Logger logger = LoggerFactory.getLogger(JobExecutor.class);
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @Autowired
    private JobLogService jobLogService;
    
    private final ExecutorService executorService = Executors.newCachedThreadPool(r -> {
        Thread t = new Thread(r, "JobExecutor-" + threadCounter.incrementAndGet());
        t.setDaemon(true);
        return t;
    });
    
    private static final AtomicInteger threadCounter = new AtomicInteger(0);
    
    public void execute(JobInfo jobInfo, JobLog jobLog) {
        CompletableFuture.runAsync(() -> {
            executeJob(jobInfo, jobLog);
        }, executorService);
    }
    
    private void executeJob(JobInfo jobInfo, JobLog jobLog) {
        long startTime = System.currentTimeMillis();
        boolean success = false;
        String errorMsg = null;
        
        try {
            jobLog.setId(jobLogService.insert(jobLog));
            
            JobHandler jobHandler = getJobHandler(jobInfo.getJobClassName());
            
            if (jobHandler == null) {
                throw new RuntimeException("Job handler not found: " + jobInfo.getJobClassName());
            }
            
            int retryCount = 0;
            int maxRetries = jobInfo.getExecutorFailRetryCount() != null ? 
                jobInfo.getExecutorFailRetryCount() : 0;
            
            while (retryCount <= maxRetries) {
                try {
                    if (jobInfo.getExecutorTimeout() != null && jobInfo.getExecutorTimeout() > 0) {
                        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                            try {
                                jobHandler.execute(jobInfo, jobLog);
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        });
                        
                        future.get(jobInfo.getExecutorTimeout(), java.util.concurrent.TimeUnit.SECONDS);
                    } else {
                        jobHandler.execute(jobInfo, jobLog);
                    }
                    
                    success = true;
                    break;
                    
                } catch (Exception e) {
                    retryCount++;
                    errorMsg = "Attempt " + retryCount + " failed: " + e.getMessage();
                    logger.warn("Job execution failed (attempt {}/{}): jobId={}, error={}", 
                        retryCount, maxRetries + 1, jobInfo.getId(), e.getMessage());
                    
                    if (retryCount <= maxRetries) {
                        Thread.sleep(1000 * retryCount);
                    }
                }
            }
            
        } catch (Exception e) {
            success = false;
            errorMsg = "Job execution error: " + e.getMessage();
            logger.error("Job execution failed: jobId={}", jobInfo.getId(), e);
        } finally {
            updateJobLog(jobLog, success, errorMsg, startTime);
        }
    }
    
    private JobHandler getJobHandler(String jobClassName) {
        try {
            if (jobClassName.contains(".")) {
                Class<?> clazz = Class.forName(jobClassName);
                Object bean = applicationContext.getBean(clazz);
                if (bean instanceof JobHandler) {
                    return (JobHandler) bean;
                }
            } else {
                try {
                    Object bean = applicationContext.getBean(jobClassName);
                    if (bean instanceof JobHandler) {
                        return (JobHandler) bean;
                    }
                } catch (Exception e) {
                    // ignore, try next approach
                }
            }
            
            String[] beanNames = applicationContext.getBeanNamesForType(JobHandler.class);
            for (String beanName : beanNames) {
                JobHandler handler = (JobHandler) applicationContext.getBean(beanName);
                if (handler.getClass().getSimpleName().equals(jobClassName) ||
                    handler.getClass().getName().equals(jobClassName)) {
                    return handler;
                }
            }
            
        } catch (Exception e) {
            logger.error("Error getting job handler: {}", jobClassName, e);
        }
        
        return null;
    }
    
    private void updateJobLog(JobLog jobLog, boolean success, String errorMsg, long startTime) {
        try {
            jobLog.setHandleTime(new Timestamp(System.currentTimeMillis()));
            jobLog.setHandleCode(success ? 200 : 500);
            jobLog.setHandleMsg(success ? "Job execution completed successfully" : errorMsg);
            
            jobLogService.updateHandleInfo(jobLog);
            
            logger.debug("Job execution completed: jobId={}, success={}, duration={}ms", 
                jobLog.getJobId(), success, System.currentTimeMillis() - startTime);
                
        } catch (Exception e) {
            logger.error("Error updating job log: jobLogId={}", jobLog.getId(), e);
        }
    }
}